/*******************************************************************************
 *  Copyright (c) 2000, 2015 IBM Corporation and others.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Contributors:
 *     IBM Corporation - initial API and implementation
 *     Michael Williamson (eclipse-bugs@magnaworks.com) - patch (see Bugzilla #92545)
 *
 *******************************************************************************/
package org.eclipse.ui.forms.widgets;

import java.util.Hashtable;

import org.eclipse.core.runtime.Assert;
import org.eclipse.rap.rwt.theme.BoxDimensions;
import org.eclipse.rap.rwt.theme.ControlThemeAdapter;
import org.eclipse.swt.SWT;
//import org.eclipse.swt.events.PaintEvent;
//import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.widgets.IWidgetGraphicsAdapter;
import org.eclipse.swt.internal.widgets.controlkit.ControlThemeAdapterImpl;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.internal.forms.widgets.FormImages;
//import org.eclipse.ui.internal.forms.widgets.FormUtil;

/**
 * A variation of the expandable composite that adds optional description below
 * the title. Section is often used as a basic building block in forms because
 * it provides for logical grouping of information.
 * <p>
 * In case of the TITLE_BAR style, Section renders the title bar in a way
 * compatible with the rest of the workbench. Since it is a widget, all the
 * colors must be supplied directly. When created by the form toolkit, these
 * colors are supplied by the toolkit. The toolkit initializes these colors
 * based on the system colors. For this reason, it is recommended to create the
 * section by the toolkit instead of through its own constructor.
 * <p>
 * Since 3.1, it is possible to set a control to be used for section
 * description. If used, <code>DESCRIPTION</code> style should not be set. A
 * typical way to take advantage of the new method is to set an instance of
 * <code>FormText</code> to provide for hyperlinks and images in the
 * description area.
 *
 * @since 1.0
 */
@SuppressWarnings("restriction")
public class Section extends ExpandableComposite {
    /**
     * Description style. If used, description will be rendered below the title.
     */
    public static final int DESCRIPTION = 1 << 7;

    private Control descriptionControl;

    private Control separator;

    private Hashtable titleColors;

    private static final String COLOR_BG = "bg"; //$NON-NLS-1$

    private static final String COLOR_GBG = "gbg"; //$NON-NLS-1$

    private static final String COLOR_BORDER = "border"; //$NON-NLS-1$

    /**
     * Creates a new section instance in the provided parent.
     *
     * @param parent
     *            the parent composite
     * @param style
     *            the style to use
     */
    public Section(Composite parent, int style) {
        this(parent, SWT.NULL, style);
    }

    Section(Composite parent, int cstyle, int style) {
        super(parent, cstyle | getBackgroundStyle(style), style);
        // RAP [rh] RIGHT_TO_LEFT not implemented
        int rtl = cstyle /* & SWT.RIGHT_TO_LEFT */;
        if ((style & DESCRIPTION) != 0) {
            descriptionControl = new Text(this, SWT.READ_ONLY | SWT.WRAP | rtl);
        }
        if ((style & TITLE_BAR) != 0) {
            Listener listener = new Listener() {
                public void handleEvent(Event e) {
                    Image image = Section.super.getBackgroundImage();
                    if (image != null) {
                        FormImages.getInstance().markFinished(image);
                    }
                    Section.super.setBackgroundImage(null);
// RAP [if] Title bar background gradient rendering
                    if( e.type != SWT.Dispose ) {
                      applyBackgroundGradient();
                    }
// ENDRAP [if]
                }
            };
            addListener(SWT.Dispose, listener);
            addListener(SWT.Resize, listener);
        }
    }

    private static int getBackgroundStyle(int estyle) {
// RAP [rh] NO_BACKGROUND not available
//      return ((estyle & TITLE_BAR) != 0) ? SWT.NO_BACKGROUND : SWT.NULL;
        return SWT.NULL;
    }

    protected void internalSetExpanded(boolean expanded) {
        super.internalSetExpanded(expanded);
        if ((getExpansionStyle() & TITLE_BAR) != 0) {
            if (!expanded)
                super.setBackgroundImage(null);
        }
        reflow();
    }

    /**
     * Reflows this section and all the parents up the hierarchy until a
     * SharedScrolledComposite is reached.
     */
    protected void reflow() {
        Composite c = this;
        while (c != null) {
            c.setRedraw(false);
            c = c.getParent();
            if (c instanceof SharedScrolledComposite || c instanceof Shell) {
                break;
            }
        }
        c = this;
        while (c != null) {
            c.layout(true);
            c = c.getParent();
            if (c instanceof SharedScrolledComposite) {
                ((SharedScrolledComposite) c).reflow(true);
                break;
            }
        }
        c = this;
        while (c != null) {
            c.setRedraw(true);
            c = c.getParent();
            if (c instanceof SharedScrolledComposite || c instanceof Shell) {
                break;
            }
        }
    }

    /**
     * Sets the description text. Has no effect if DESCRIPTION style was not
     * used to create the control.
     *
     * @param description
     */
    public void setDescription(String description) {
        if (descriptionControl instanceof Text)
            ((Text) descriptionControl).setText(description);
    }

    /**
     * Returns the current description text.
     *
     * @return description text or <code>null</code> if DESCRIPTION style was
     *         not used to create the control.
     */
    public String getDescription() {
        if (descriptionControl instanceof Text)
            return ((Text) descriptionControl).getText();
        return null;
    }

    /**
     * Sets the separator control of this section. The separator must not be
     * <samp>null </samp> and must be a direct child of this container. If
     * defined, separator will be placed below the title text and will remain
     * visible regardless of the expansion state.
     *
     * @param separator
     *            the separator that will be placed below the title text.
     */
    public void setSeparatorControl(Control separator) {
        Assert.isTrue(separator != null && separator.getParent().equals(this));
        this.separator = separator;
    }

    /**
     * Returns the control that is used as a separator betweeen the title and
     * the client, or <samp>null </samp> if not set.
     *
     * @return separator control or <samp>null </samp> if not set.
     */
    public Control getSeparatorControl() {
        return separator;
    }

    /**
     * Sets the background of the section.
     *
     * @param bg
     *            the new background
     */
    public void setBackground(Color bg) {
        super.setBackground(bg);
        if (descriptionControl != null
                && (getExpansionStyle() & DESCRIPTION) != 0)
            descriptionControl.setBackground(bg);
// RAP [if] Title bar background gradient rendering
        applyBackgroundGradient();
// ENDRAP [if]
    }

    /**
     * Sets the foreground of the section.
     *
     * @param fg
     *            the new foreground.
     */
    public void setForeground(Color fg) {
        super.setForeground(fg);
        if (descriptionControl != null
                && (getExpansionStyle() & DESCRIPTION) != 0)
            descriptionControl.setForeground(fg);
    }

    /**
     * Returns the control used to render the description. In 3.1, this method
     * was promoted to public.
     *
     * @return description control or <code>null</code> if DESCRIPTION style
     *         was not used to create the control and description control was
     *         not set by the client.
     * @see #setDescriptionControl(org.eclipse.swt.widgets.Control)
     */
    public Control getDescriptionControl() {
        return descriptionControl;
    }

    /**
     * Sets the description control of this section. The control must not be
     * <samp>null</samp> and must be a direct child of this container. If
     * defined, contol will be placed below the title text and the separator and
     * will be hidden int he collapsed state.
     * <p>
     * This method and <code>DESCRIPTION</code> style are mutually exclusive.
     * Use the method only if you want to create the description control
     * yourself.
     *
     * @param descriptionControl
     *            the control that will be placed below the title text.
     */
    public void setDescriptionControl(Control descriptionControl) {
        Assert.isTrue((getExpansionStyle() & DESCRIPTION) == 0);
        Assert.isTrue(descriptionControl != null
                && descriptionControl.getParent().equals(this));
        this.descriptionControl = descriptionControl;
    }

    /**
     * Sets the color of the title bar border when TITLE_BAR style is used.
     *
     * @param color
     *            the title bar border color
     */
    public void setTitleBarBorderColor(Color color) {
        putTitleBarColor(COLOR_BORDER, color);
    }

    /**
     * Sets the color of the title bar background when TITLE_BAR style is used.
     * This color is used as a starting color for the vertical gradient.
     *
     * @param color
     *            the title bar border background
     */
    public void setTitleBarBackground(Color color) {
        putTitleBarColor(COLOR_BG, color);
// RAP [if] Title bar background gradient rendering
        applyBackgroundGradient();
// ENDRAP [if]
    }

    /**
     * Sets the color of the title bar gradient background when TITLE_BAR style
     * is used. This color is used at the height where title controls end
     * (toggle, tool bar).
     *
     * @param color
     *            the title bar gradient background
     */
    public void setTitleBarGradientBackground(Color color) {
        putTitleBarColor(COLOR_GBG, color);
    }

    /**
     * Returns the title bar border color when TITLE_BAR style is used.
     *
     * @return the title bar border color
     */
    public Color getTitleBarBorderColor() {
        if (titleColors == null)
            return null;
        return (Color) titleColors.get(COLOR_BORDER);
    }

    /**
     * Returns the title bar gradient background color when TITLE_BAR style is
     * used.
     *
     * @return the title bar gradient background
     */
    public Color getTitleBarGradientBackground() {
        if (titleColors == null)
            return null;
        if ((getExpansionStyle() & SHORT_TITLE_BAR) != 0)
            return getBackground();
        return (Color) titleColors.get(COLOR_GBG);
    }

    /**
     * Returns the title bar background when TITLE_BAR style is used.
     *
     * @return the title bar background
     */
    public Color getTitleBarBackground() {
        if (titleColors == null)
            return null;
        return (Color) titleColors.get(COLOR_BG);
    }

    private void putTitleBarColor(String key, Color color) {
        if (color == null)
            return;
        if (titleColors == null)
            titleColors = new Hashtable();
        titleColors.put(key, color);
    }

// RAP [rh] missing Paint events
//  protected void onPaint(PaintEvent e) {
//      Color bg = null;
//      Color fg = null;
//      Color border = null;
//
//      GC gc = e.gc;
//      Image buffer = null;
//      Rectangle bounds = getClientArea();
//
//      if ((getExpansionStyle() & TITLE_BAR) != 0) {
//          buffer = new Image(getDisplay(), bounds.width, bounds.height);
//          buffer.setBackground(getBackground());
//          gc = new GC(buffer);
//      }
//      if (titleColors != null) {
//          bg = (Color) titleColors.get(COLOR_BG);
//          fg = getTitleBarForeground();
//          border = (Color) titleColors.get(COLOR_BORDER);
//      }
//      if (bg == null)
//          bg = getBackground();
//      if (fg == null)
//          fg = getForeground();
//      if (border == null)
//          border = fg;
//      int theight = 0;
//      int gradientheight = 0;
//      int tvmargin = IGAP;
//      if ((getExpansionStyle() & TITLE_BAR) != 0) {
//          Point tsize = null;
//          Point tcsize = null;
//          if (toggle != null)
//              tsize = toggle.getSize();
//          int twidth = bounds.width - marginWidth - marginWidth;
//          if (tsize != null)
//              twidth -= tsize.x + IGAP;
//          if (getTextClient() != null)
//              tcsize = getTextClient().getSize();
//          if (tcsize != null)
//              twidth -= tcsize.x + IGAP;
//          Point size = textLabel.getSize();
//          if (tsize != null)
//              theight += Math.max(theight, tsize.y);
//          gradientheight = theight;
//          if (tcsize != null) {
//              theight = Math.max(theight, tcsize.y);
//          }
//          theight = Math.max(theight, size.y);
//          gradientheight = Math.max(gradientheight, size.y);
//          theight += tvmargin + tvmargin;
//          gradientheight += tvmargin + tvmargin;
//      } else {
//          theight = 5;
//      }
//      if ((getExpansionStyle() & TITLE_BAR) != 0) {
//          if (getBackgroundImage() == null)
//              updateHeaderImage(bg, bounds, gradientheight, theight);
//          gc.setBackground(getBackground());
//          gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
//          drawBackground(gc, bounds.x, bounds.y, bounds.width, theight);
//          if (marginWidth > 0) {
//              // fix up margins
//              gc.setBackground(getBackground());
//              gc.fillRectangle(0, 0, marginWidth, theight);
//              gc.fillRectangle(bounds.x + bounds.width - marginWidth, 0,
//                      marginWidth, theight);
//          }
//      } else if (isExpanded()) {
//          gc.setForeground(bg);
//          gc.setBackground(getBackground());
//          gc.fillGradientRectangle(marginWidth, marginHeight, bounds.width
//                  - marginWidth - marginWidth, theight, true);
//      }
//      gc.setBackground(getBackground());
//      FormUtil.setAntialias(gc, SWT.ON);
//      // repair the upper left corner
//      gc.fillPolygon(new int[] { marginWidth, marginHeight, marginWidth,
//              marginHeight + 2, marginWidth + 2, marginHeight });
//      // repair the upper right corner
//      gc.fillPolygon(new int[] { bounds.width - marginWidth - 3,
//              marginHeight, bounds.width - marginWidth, marginHeight,
//              bounds.width - marginWidth, marginHeight + 3 });
//      gc.setForeground(border);
//      if (isExpanded() || (getExpansionStyle() & TITLE_BAR) != 0) {
//          // top left curve
//          gc.drawLine(marginWidth, marginHeight + 2, marginWidth + 2,
//                  marginHeight);
//          // top edge
//          gc.drawLine(marginWidth + 2, marginHeight, bounds.width
//                  - marginWidth - 3, marginHeight);
//          // top right curve
//          gc.drawLine(bounds.width - marginWidth - 3, marginHeight,
//                  bounds.width - marginWidth - 1, marginHeight + 2);
//      } else {
//          // collapsed short title bar
//          // top edge
//          gc.drawLine(marginWidth, marginHeight, bounds.width - 1,
//                  marginHeight);
//      }
//      if ((getExpansionStyle() & TITLE_BAR) != 0 || isExpanded()) {
//          // left vertical edge gradient
//          gc.fillGradientRectangle(marginWidth, marginHeight + 2, 1,
//                  gradientheight - 2, true);
//          // right vertical edge gradient
//          gc.fillGradientRectangle(bounds.width - marginWidth - 1,
//                  marginHeight + 2, 1, gradientheight - 2, true);
//      }
//      if ((getExpansionStyle() & TITLE_BAR) != 0) {
//          // New in 3.3 - edge treatmant
//          gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WHITE));
//          gc.drawPolyline(new int[] { marginWidth + 1,
//                  marginHeight + gradientheight - 1, marginWidth + 1,
//                  marginHeight + 2, marginWidth + 2, marginHeight + 2,
//                  marginWidth + 2, marginHeight + 1,
//                  bounds.width - marginWidth - 3, marginHeight + 1,
//                  bounds.width - marginWidth - 3, marginHeight + 2,
//                  bounds.width - marginWidth - 2, marginHeight + 2,
//                  bounds.width - marginWidth - 2,
//                  marginHeight + gradientheight - 1 });
//      }
//      if (buffer != null) {
//          gc.dispose();
//          e.gc.drawImage(buffer, 0, 0);
//          buffer.dispose();
//      }
//  }
//
//  private void updateHeaderImage(Color bg, Rectangle bounds, int theight,
//          int realtheight) {
//      Image image = FormImages.getInstance().getGradient(getDisplay(), getBackground(), bg, realtheight, theight, marginHeight);
//      super.setBackgroundImage(image);
//  }

    /**
     * Background image is used for the title gradient - does nothing.
     */
    public final void setBackgroundImage(Image image) {
    }

// RAP [if] Title bar background gradient rendering
    private final void applyBackgroundGradient() {
      if( ( getExpansionStyle() & TITLE_BAR ) != 0 ) {
        // Code start - onPaint
        Color bg = null;
        if (titleColors != null) {
            bg = (Color) titleColors.get(COLOR_BG);
        }
        if (bg == null)
            bg = getBackground();
        Rectangle bounds = getClientArea();
        int theight = 0;
        int gradientheight = 0;
        int tvmargin = IGAP;
        Point tsize = null;
        Point tcsize = null;
        if (toggle != null)
            tsize = toggle.getSize();
        int twidth = bounds.width - marginWidth - marginWidth;
        if (tsize != null)
            twidth -= tsize.x + IGAP;
        if (getTextClient() != null)
            tcsize = getTextClient().getSize();
        if (tcsize != null)
            twidth -= tcsize.x + IGAP;
        Point size = textLabel.getSize();
        if (tsize != null)
            theight += Math.max(theight, tsize.y);
        gradientheight = theight;
        if (tcsize != null) {
            theight = Math.max(theight, tcsize.y);
        }
        theight = Math.max(theight, size.y);
        gradientheight = Math.max(gradientheight, size.y);
        theight += tvmargin + tvmargin;
        gradientheight += tvmargin + tvmargin;
        // Code end - onPaint

        Object adapter = getAdapter( IWidgetGraphicsAdapter.class );
        IWidgetGraphicsAdapter gfxAdapter = ( IWidgetGraphicsAdapter )adapter;
        Color[] gradientColors = new Color[] {
          bg,
          getBackground(),
          getBackground()
        };
        int gradientPercent = 0;
        if( bounds.height != 0 ) {
          gradientPercent = gradientheight * 100 / bounds.height;
          if( gradientPercent > 100 ) {
            gradientPercent = 100;
          }
        }
        int[] percents = new int[] { 0, gradientPercent, 100 };
        gfxAdapter.setBackgroundGradient( gradientColors, percents, true );
        gfxAdapter.setRoundedBorder( 1, getBackground(), 8, 8, 0, 0 );
      }
    }

    // Fix 1px border introduced by the titlebar rounded border
    private static final BoxDimensions ONE = new BoxDimensions( 1, 1, 1, 1 );
    private ControlThemeAdapter controlThemeAdapter;
    @Override
    public <T> T getAdapter( Class<T> adapter ) {
      if( adapter == ControlThemeAdapter.class ) {
        if( controlThemeAdapter == null ) {
          controlThemeAdapter = new ControlThemeAdapterImpl() {
            @Override
            public BoxDimensions getBorder( Control control ) {
              if( ( getExpansionStyle() & TITLE_BAR ) != 0 ) {
                return ONE;
              }
              return super.getBorder( control );
            }
          };
        }
        return ( T )controlThemeAdapter;
      }
      return super.getAdapter( adapter );
    }

// ENDRAP [if]
}
